const VNodeType = {
    //组件
    COMPONENT: 'COMPONENT',
    //普通标签
    HTML: 'HTML',
    //文本
    TEXT: 'TEXT'
}
const childTypes = {
    EMPTY: 'EMPTY',
    SINGLE: 'SINGLE',
    MULTIPLE: 'MULTIPLE'
}

function createElement(tag, data = null, children = null) {
    let flags
    if (typeof tag === 'string') {
        flags = VNodeType.HTML
    } else if (typeof tag == 'function') {
        //组件先不考虑
        flags = VNodeType.COMPONENT
    } else {
        flags = VNodeType.TEXT
    }
    //确认是否有子节点
    let childFlags = null
    if (Array.isArray(children)) {
        if (children.length === 0) {
            //没有children
            childFlags = childTypes.EMPTY
        } else {
            //多个子节点
            childFlags = childTypes.MULTIPLE
        }
    } else if (children == null) {
        childFlags = childTypes.EMPTY
    } else {
        //children是文本
        childFlags = childTypes.SINGLE
        children = createTextVNode(children)
    }

    //返回VNode对象
    return {
        flags,
        tag,
        data,
        key: data && data.key,
        children,
        childFlags,
        el: null
    }
}

//  创建文本节点
function createTextVNode(text) {
    return {
        flags: VNodeType.TEXT,
        tag: null,
        data: null,
        // 纯文本类型的 VNode，其 children 属性存储的是与之相符的文本内容
        children: text,
        childFlags: childTypes.EMPTY
    }
}

function render(VNode, container) {
    const preNode = container.vnode
    if (preNode == null) {
        //没有旧的vnode，使用`mount` 函数挂载全新的 VNode
        mount(VNode, container)
    } else {
        //有旧的Vnode，进行更新
        Patch(preNode, VNode, container)
    }
    //进行上次执行返回Vnode的缓存
    container.vnode = VNode
}

function mount(VNode, container, refNode) {
    const { flags } = VNode
    if (flags == VNodeType.HTML) {
        //挂载普通标签
        mountElement(VNode, container, refNode)
    } else if (flags == VNodeType.TEXT) {
        //挂着纯文本
        mountText(VNode, container)
    }
}

function mountText(VNode, container) {
    const el = document.createTextNode(VNode.children)
    VNode.el = el
    container.appendChild(el)
}

function mountElement(vnode, container, refNode) {
    const el = document.createElement(vnode.tag)
    vnode.el = el
    const data = vnode.data
    if (data) {
        for (let key in data) {
            patchData(el, key, null, data[key])
        }
    }
    const childFlag = vnode.childFlags
    if (childFlag != childTypes.EMPTY) {
        if (childFlag == childTypes.MULTIPLE) {
            for (let i = 0; i < vnode.children.length; i++) {
                mount(vnode.children[i], vnode.el)
            }
        } else if (childFlag == childTypes.SINGLE) {
            mount(vnode.children, el)
        }
    }
    refNode ? container.insertBefore(el, refNode) : container.appendChild(el)
}

function patchData(el, key, preVal, nextval) {
    switch (key) {
        case 'style':
            for (let key in nextval) {
                el.style[key] = nextval[key]
            }
            for (let k in preVal) {
                if (!nextval.hasOwnProperty(k)) {
                    el.style[k] = ''
                }
            }
            break
        case 'class':
            el.className = nextval
            break;
        default:
            if (key[0] === '@') {
                // 事件
                // 移除旧事件
                if (preVal) {
                    el.removeEventListener(key.slice(1), preVal)
                }
                // 添加新事件
                if (nextval) {
                    el.addEventListener(key.slice(1), nextval)
                }
            } else {
                // 当做 Attr 处理
                el.setAttribute(key, nextval)
            }
            break
    }
}

function Patch(preNode, nextNode, container) {
    const nextFlags = nextNode.flags
    const preFlags = preNode.flags
    if (nextFlags !== preFlags) {
        //直接替换
        replaceVnode(preNode, nextNode, container)
    } else if (nextFlags == VNodeType.HTML) {
        patchElement(preNode, nextNode, container)
    } else if (nextFlags == VNodeType.TEXT) {
        replaveText(preNode, nextNode)
    }
}

function replaceVnode(preNode, nextNode, container) {
    container.removeChild(preNode.el)
    mount(nextNode, container)
}

function replaveText(preNode, nextNode) {
    if (preNode.children !== nextNode.children) {
        prevVNode.el.nodeValue = nextNode.children
    }
}

function patchElement(preNode, nextNode, container) {
    // 如果新旧 VNode 描述的是不同的标签，则调用 replaceVNode 函数使用新的 VNode 替换旧的 VNode
    if (preNode.tag !== nextNode.tag) {
        replaceVnode(preNode, nextNode, container)
        return
    }

    //如果标签也相等
    const el = (nextNode.el = preNode.el)
    const preData = preNode.data
    const nextData = nextNode.data
    //新增属性
    if (nextData) {
        for (let key in nextData) {
            const preValue = preData[key]
            const nextValue = nextData[key]
            patchData(el, key, preValue, nextValue)
        }
    }
    //删除旧的属性
    if (preData) {
        for (let key in preData) {
            const preValue = preData[key]
            if (preValue && !nextData.hasOwnProperty(key)) {
                patchData(el, key, preValue, null)
            }
        }
    }
    //调用patchChildren递归更新子节点
    patchChildren(
        preNode.childFlags, //旧的VNode子节点类型
        nextNode.childFlags, //新的VNode子节点类型
        preNode.children,   //旧的VNode子节点
        nextNode.children,   //新的VNode子节点
        el //当前标签元素，这些子节点的父节点
    )
}

function patchChildren(preChildFlags, nextChildFlags, preChildren, nextChildren, el) {
    switch (preChildFlags) {
        // 旧的 children 是单个子节点，会执行该 case 语句块
        case childTypes.SINGLE:
            switch (nextChildFlags) {
                case childTypes.SINGLE:
                    //新的节点和旧的节点都是单个
                    Patch(preChildren, nextChildren, el)
                    break;
                case childTypes.EMPTY:
                    //旧的节点是单个，新的节点为空(单个节点定义的问文本节点)
                    el.removeChild(preChildren.el)
                    break;
                default:
                    //旧的节点是单个，新的节点是多个
                    el.removeChild(preChildren.el)
                    for (let i = 0; i < nextChildren.length; i++) {
                        mount(nextChildren[i], el)
                    }
                    break;
            }
            break;
        case childTypes.EMPTY:
            switch (nextChildFlags) {
                case childTypes.SINGLE:
                    //旧的节点为空，新的节点是单个(文本节点)
                    mount(nextChildren, el)
                    break;
                case childTypes.EMPTY:
                    //旧的节点为空，新的节点为空
                    break;
                default:
                    //旧的节点为空，新的节点为多个
                    for (let i = 0; i < nextChildren.length; i++) {
                        mount(nextChildren[i], el)
                    }
                    break;
            }
            break;
        default:
            switch (nextChildFlags) {
                case childTypes.SINGLE:
                    //旧的节点为多个，新的节点为一个(文本节点)
                    for (let i = 0; i < preChildren.length; i++) {
                        el.removeChild(preChildren[i].el)
                    }
                    mount(nextChildren, el)
                    break;
                case childTypes.EMPTY:
                    //旧的节点为多个，新的节点为空
                    for (let i = 0; i < preChildren.length; i++) {
                        el.removeChild(preChildren[i].el)
                    }
                    break;
                default:
                    //旧的节点为多个，新的节点为多个(diff算法)
                    let lastIndex = 0
                    for (let i = 0; i < nextChildren.length; i++) {
                        const nextVNode = nextChildren[i]
                        let j=0, find = false
                        for ( j ; j < preChildren.length; j++) {
                            const preVNode = preChildren[j]
                            //判断新旧节点是否有Key相等
                            if (nextVNode.key === preVNode.key) {
                                find = true
                                //进行新旧节点的patch
                                Patch(preVNode, nextVNode, el)
                                if (j < lastIndex) {
                                    const refNode = nextChildren[i - 1].el.nextSibling
                                    el.insertBefore(preVNode.el, refNode)
                                    break
                                } else {
                                    lastIndex = j
                                }
                            } 
                        }
                        if (!find) {
                            //没有找到相同的Key,挂着新的节点
                            const refNode = i - 1 < 0
                                ? preChildren[0].el
                                : nextChildren[i - 1].el.nextSibling
                            mount(nextVNode, el, refNode)
                        }
                    }
                    //移除已经不存在的节点
                    for (let i = 0; i < preChildren.length; i++) {
                        const preVNode = preChildren[i]
                        const has = nextChildren.find(nextNode => nextNode.key == preVNode.key)
                        if (!has) {
                            el.removeChild(preVNode.el)
                        }
                    }
                    break;
            }
            break;
    }
}